﻿using Dapper;
using SIG.DAL.Dapper.Model;
using SIG.Infrastructure.Cache;
using SIG.Infrastructure.Configs;
using SIG.Infrastructure.Extensions;
using SIG.Infrastructure.Helper;
using SIG.Resources.Admin;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SIG.DAL.Dapper.Repositories
{
    public class MenuRepository
    {
        private DbConnection _connection;
        private readonly ICacheService _cacheService;
        public MenuRepository()
        {
            _cacheService = new CacheService();
        }
        /// <summary>
        /// 前端二级菜单获取
        /// </summary>
        /// <param name="categoryId"></param>
        /// <returns></returns>
        public List<Menu> GetFrontMenus(int categoryId)
        {
            List<Menu> result = new List<Menu>();
            using (_connection = Utilities.GetOpenConnection())
            {               
               var menuList = _connection.GetList<Menu>(new { CategoryId = categoryId });
                foreach(var item in menuList.Where(m=>m.Active && m.ParentId == null).OrderBy(m=>m.Importance))
                {
                    item.ChildMenus = menuList.Where(m => m.Active && m.ParentId == item.Id).OrderBy(m => m.Importance).ToList();
                    result.Add(item);
                }
            }
            return result;
        }

        /// <summary>
        /// 按角色获取菜单
        /// </summary>
        /// <param name="roleId"></param>
        /// <returns></returns>
        public IEnumerable<Menu> GetMenusByRoleId(int roleId)
        {
            IEnumerable<Menu> result;
            using (_connection = Utilities.GetOpenConnection())
            {
                var sql = "SELECT DISTINCT MenuSet.*            " +
                    " FROM RoleMenus INNER JOIN           " +
                    "  MenuSet ON RoleMenus.MenuId = MenuSet.Id " +
                    "   WHERE(RoleMenus.RoleId = @RoleId)";
                result = _connection.Query<Menu>(sql, new { RoleId = roleId });
            }
            return result;
        }

        /// <summary>
        /// 按用户的角色获取菜单
        /// </summary>
        /// <param name="userId"></param>
        /// <returns></returns>
        public IEnumerable<Menu> GetMenusByUserId(Guid userId)
        {
            IEnumerable<Menu> result;
            using (_connection = Utilities.GetOpenConnection())
            {
                var sql = "SELECT DISTINCT MenuSet.*            " +
                    " FROM RoleMenus INNER JOIN           " +
                    "  MenuSet ON RoleMenus.MenuId = MenuSet.Id INNER JOIN     " +
                    "       Users INNER JOIN           UserRoles ON Users.Id = UserRoles.UserId INNER JOIN     " +
                    "        Roles ON UserRoles.RoleId = Roles.Id ON RoleMenus.RoleId = Roles.Id     " +
                    "   WHERE(Users.Id = @UserId)";
                result =  _connection.Query<Menu>(sql, new { UserId = userId });
            }
            return result;
        }

        /// <summary>
        /// 判断菜单权限
        /// </summary>
        /// <param name="area"></param>
        /// <param name="controller"></param>
        /// <param name="action"></param>
        /// <param name="Menus"></param>
        /// <returns></returns>
        public bool IsInMenu(string area, string controller, string action, IEnumerable<Menu> Menus)
        {
            if (Menus == null)
                return false;

            area = string.IsNullOrEmpty(area) ? area : area.ToLower();
            controller = string.IsNullOrEmpty(controller) ? controller : controller.ToLower();
            action = string.IsNullOrEmpty(action) ? action : action.ToLower();

            foreach (var item in Menus)
            {
                var mArea = string.IsNullOrEmpty(item.Area) ? item.Area : item.Area.ToLower();
                var mController = string.IsNullOrEmpty(item.Controller) ? item.Controller : item.Controller.ToLower();
                var mAction = string.IsNullOrEmpty(item.Action) ? item.Action : item.Action.ToLower();

                bool result = (mAction == action && mController == controller && mArea == area);
                 
                if (result)
                {
                    return true;
                }
            }
            
            return false;

        }

        /// <summary>
        /// 按分类ID获取菜单并缓存
        /// </summary>
        /// <param name="categoryId"></param>
        /// <returns></returns>
        public IEnumerable<Menu> GetMenusByCategoryIdWithCache(int categoryId)
        {
            var key = $"{EntityNames.Menu}S_SetRole_{categoryId}";

            IEnumerable<Menu> result;
            if (SettingsManager.Menu.EnableCaching)
            {
                if (_cacheService.IsSet(key))
                {
                    result = (IEnumerable<Menu>)_cacheService.Get(key);
                }
                else
                {
                    result = GetMenusByCategoryId(categoryId);
                    _cacheService.Set(key, result, SettingsManager.Menu.CacheDuration);
                }
            }
            else
            {
                result = GetMenusByCategoryId(categoryId);
            }

            return result;
        }

        /// <summary>
        /// 按分类ID获取菜单并层级化（角色菜单权限）
        /// </summary>
        /// <param name="categoryId"></param>
        /// <returns></returns>
        public IEnumerable<Menu> GetLevelMenusByCategoryId(int categoryId)
        {
            var list = GetMenusByCategoryIdWithCache(categoryId);
            var resultMenus = list.Where(m => m.ParentId == null);
            RecursiveLoadRoleMenus(resultMenus, list);
            return resultMenus;
        }

        private void RecursiveLoadRoleMenus(IEnumerable<Menu> parentMenus, IEnumerable<Menu> allMenus)
        {            
            foreach (var item in parentMenus)
            {
                item.ChildMenus = allMenus.Where(m => m.ParentId == item.Id);
                RecursiveLoadRoleMenus(item.ChildMenus, allMenus);
            }            
        }

        /// <summary>
        /// 按分类ID获取菜单
        /// </summary>
        /// <param name="categoryId"></param>
        /// <returns></returns>
        private IEnumerable<Menu> GetMenusByCategoryId(int categoryId)
        {
            IEnumerable<Menu> result;
            using (_connection = Utilities.GetOpenConnection())
            {
                result = _connection.GetList<Menu>("where CategoryId = @CategoryId ORDER BY Importance", new { CategoryId = categoryId });
            }
            return result;
        }

        /// <summary>
        /// 按分类ID获取左侧显示菜单
        /// </summary>
        /// <param name="categoryId"></param>
        /// <returns></returns>
        public IEnumerable<Menu> GetShowMenus(int categoryId)
        {
           
            var key = $"{EntityNames.Menu}S_SHOW_{categoryId}";

            IEnumerable<Menu> result;
            if (SettingsManager.Menu.EnableCaching)
            {
                if (_cacheService.IsSet(key))
                {
                    result = (IEnumerable<Menu>)_cacheService.Get(key);
                }
                else
                {                   
                    result = GetMenusByCategoryIdAndMenuType(categoryId);
                    _cacheService.Set(key, result, SettingsManager.Menu.CacheDuration);
                }
            }
            else
            {
                result = GetMenusByCategoryIdAndMenuType(categoryId);
            }
            
            return result;
        }

        
        private IEnumerable<Menu> GetMenusByCategoryIdAndMenuType(int categoryId)
        {
            IEnumerable<Menu> result;
            using (_connection = Utilities.GetOpenConnection())
            {
                result = _connection.GetList<Menu>("where CategoryId = @CategoryId and (MenuType=1 or MenuType=3) ORDER BY Importance", new { CategoryId = categoryId });
            }
            return result;
        }

        /// <summary>
        /// 获取需要高亮的菜单ID
        /// </summary>
        /// <returns></returns>
        public Menu GetCurrenMenu()
        {
            string area = Site.CurrentArea(), controller = Site.CurrentController(), action = Site.CurrentAction();

            var menus = GetAllMenusByCategoryId(SettingsManager.Menu.BackMenuCId);

            if (menus == null)
                return null;

            Menu vMenu = menus.FirstOrDefault(m => area.Equals(m.Area, StringComparison.OrdinalIgnoreCase)
            && controller.Equals(m.Controller, StringComparison.OrdinalIgnoreCase)
            && action.Equals(m.Action, StringComparison.OrdinalIgnoreCase));


            if (vMenu == null)
                return null;

          //  if (vMenu.Active)
                return vMenu;
          //  return RecursiveLoadMenu(vMenu.ParentId);

        }
        private Menu RecursiveLoadMenu(int? parentId)
        {
            var menus = GetAllMenusByCategoryId(SettingsManager.Menu.BackMenuCId);
            Menu vMenu = menus.Where(m => m.ParentId == parentId && m.MenuType == 1).FirstOrDefault();

            vMenu.ParentMenu = menus.FirstOrDefault(m => m.Id == vMenu.ParentId);

            if (vMenu.ParentMenu != null && (vMenu.ParentMenu.MenuType != 1 || !vMenu.ParentMenu.Active))
            {
                return RecursiveLoadMenu(vMenu.ParentId);
            }
            return vMenu.ParentMenu;
        }


        /// <summary>
        /// 按分类获取所有菜单
        /// </summary>
        /// <param name="categoryId"></param>
        /// <returns></returns>
        public IEnumerable<Menu> GetAllMenusByCategoryId(int categoryId)
        {
            IEnumerable<Menu> result;
            using (_connection = Utilities.GetOpenConnection())
            {
                result = _connection.GetList<Menu>("where (CategoryId = @CategoryId) order by Importance asc", new { CategoryId = categoryId });
            }
            return result;
        }

        /// <summary>
        /// 面包屑
        /// </summary>
        /// <param name="categoryId"></param>
        /// <returns></returns>
        public List<Menu> CurrenMenuCrumbs(int categoryId)
        {
            string area = Site.CurrentArea(), controller = Site.CurrentController(), action = Site.CurrentAction();

            var rource = GetShowMenus(categoryId).ToList();
            List<Menu> menus = new List<Menu>();

            Menu vMenu = rource.FirstOrDefault(m => area.Equals(m.Area, StringComparison.OrdinalIgnoreCase)
            && controller.Equals(m.Controller, StringComparison.OrdinalIgnoreCase)
            && action.Equals(m.Action, StringComparison.OrdinalIgnoreCase));

            if (vMenu != null)
                RecursiveLoad(vMenu, menus);

            return menus;
        }

        /// <summary>
        /// 递归获取父项
        /// </summary>
        /// <param name="vMenu"></param>
        /// <param name="Parents"></param>
        private void RecursiveLoad(Menu vMenu, List<Menu> Parents)
        {           
            Parents.Insert(0, vMenu);
            if (vMenu.ParentId != null)
            {
                var rource = GetShowMenus(vMenu.CategoryId).ToList();
                Menu parentMenu = rource.FirstOrDefault(m => m.Id == vMenu.ParentId);
                if (parentMenu != null)
                    RecursiveLoad(parentMenu, Parents);
            }
        }

        public Menu GetById(int id)
        {
            Menu result;
            using (_connection = Utilities.GetOpenConnection())
            {
                result = _connection.Get<Menu>(id);
            }
            return result;
        }

        public int GetMenuCount(int menuId)
        {
            int result;
            using (_connection = Utilities.GetOpenConnection())
            {
                var query = "with MenuSet_tree as ( " +
  "select MenuSet.id as top_MenuSet_id, MenuSet.id as MenuSet_id " +
  " from MenuSet union all   select top_MenuSet_id, MenuSet.id   from MenuSet_tree " +
  "     join MenuSet on MenuSet.ParentId = MenuSet_tree.MenuSet_id)  " +
"select count(*) as menu_count from MenuSet_tree where top_MenuSet_id = @menuId";
                result = (int)_connection.ExecuteScalar(query,new { menuId = menuId });
            }
            return result;
        }

        public int? Create(Menu model)
        {
            int? result;
            using (_connection = Utilities.GetOpenConnection())
            {
                result = _connection.Insert(model);
                _cacheService.Invalidate(EntityNames.Menu);
            }
            return result;
        }
        public int? CreateAndSort(Menu model)
        {
            int? result;
            using (_connection = Utilities.GetOpenConnection())
            {
                string query = "";
                if (model.ParentId > 0)
                {
                    query = "declare @import int; set @import = (select Importance from MenuSet where id = @ParentId);" +
               " with MenuSet_tree as (select MenuSet.id as top_MenuSet_id, MenuSet.id as MenuSet_id " +
 " from MenuSet union all   select top_MenuSet_id, MenuSet.id  from MenuSet_tree " +
      " join MenuSet on MenuSet.ParentId = MenuSet_tree.MenuSet_id ) " +
      " select @import = count(*) + @import from MenuSet_tree where top_MenuSet_id = @ParentId; " +
" update MenuSet set Importance = Importance + 1 where Importance >= @import;" +
"INSERT INTO [MenuSet]([Title],[Url],[Importance],[Hidden],[Active],[Iconfont],[ParentId],[MenuType] " +
        " ,[Area],[Controller],[Action],[CategoryId],[CreatedDate],[CreatedBy],[UpdatedDate],[UpdatedBy],[IsExpand],[LayoutLevel]) " +
   "VALUES (@Title, @Url, @import, @Hidden, @Active, @Iconfont, @ParentId, @MenuType " +
         ", @Area, @Controller, @Action, @CategoryId, @CreatedDate, @CreatedBy " +
         ", @UpdatedDate, @UpdatedBy, @IsExpand  , @LayoutLevel)";
                }
                else
                {
                    query = "declare @import int; set @import = (select count(*) from MenuSet where CategoryId = @CategoryId);" +              
"INSERT INTO [MenuSet]([Title],[Url],[Importance],[Hidden],[Active],[Iconfont],[ParentId],[MenuType] " +
        " ,[Area],[Controller],[Action],[CategoryId],[CreatedDate],[CreatedBy],[UpdatedDate],[UpdatedBy],[IsExpand],[LayoutLevel]) " +
   "VALUES (@Title, @Url, @import + 1, @Hidden, @Active, @Iconfont, @ParentId, @MenuType " +
         ", @Area, @Controller, @Action, @CategoryId, @CreatedDate, @CreatedBy " +
         ", @UpdatedDate, @UpdatedBy, @IsExpand  , @LayoutLevel)";
                }
               
                result = _connection.Execute(query,new { Title = model.Title, Url = model.Url, Hidden = model.Hidden, Active = model.Active,
                Iconfont = model.Iconfont,ParentId = model.ParentId,MenuType = model.MenuType, Area = model.Area, Controller = model.Controller,
                Action = model.Action,CategoryId = model.CategoryId,CreatedDate = model.CreatedDate,CreatedBy =model.CreatedBy,
                UpdatedDate = model.UpdatedDate,UpdatedBy = model.UpdatedBy,IsExpand = model.IsExpand,LayoutLevel = model.LayoutLevel});

                _cacheService.Invalidate(EntityNames.Menu);
            }
            return result;
        }

        public int Update(Menu model)
        {
            int result;
            using (_connection = Utilities.GetOpenConnection())
            {
                result = _connection.Update(model);
                _cacheService.Invalidate(EntityNames.Menu);
            }
            return result;
        }

        public int Delete(Menu model)
        {
            int result;
            using (_connection = Utilities.GetOpenConnection())
            {
                result = _connection.Delete(model);
                _cacheService.Invalidate(EntityNames.Menu);
            }
            return result;
        }

        public int DeleteMenuWithRoles(int id)
        {
            int result;
            using (_connection = Utilities.GetOpenConnection())
            {
                var query = "DELETE FROM [RoleMenus] WHERE MenuId = @Id; DELETE FROM MenuSet WHERE Id = @Id;";
                result = _connection.Execute(query,new { Id = id});
                _cacheService.Invalidate(EntityNames.Menu);
            }
            return result;
        }

        public int Delete(int id)
        {
            int result;
            using (_connection = Utilities.GetOpenConnection())
            {
                result = _connection.Delete<Menu>(id);
                _cacheService.Invalidate(EntityNames.Menu);
            }
            return result;
        }

        /// <summary>
        /// 向上移动
        /// </summary>
        /// <param name = "id" ></ param >
        /// < returns ></ returns >
        public int UpMoveMenu(int id)
        {
            Menu vMenu = GetById(id);
            var menuList = GetMenusByCategoryId(vMenu.CategoryId).OrderBy(m => m.Importance);

            Menu prevMenu = menuList.Where(m => m.ParentId == vMenu.ParentId &&
                m.Importance < vMenu.Importance).OrderByDescending(m => m.Importance).FirstOrDefault();

            if (prevMenu == null)
            {
                // 已经在第一位
                return 0;
            }
            var num = prevMenu.Importance  - vMenu.Importance;
            SetMenuImportance(vMenu.Id, num);
            SetMenuImportance(prevMenu.Id, -num);
         
            _cacheService.Invalidate(EntityNames.Menu);
            return 1;
        }

        /// <summary>
        /// 向下移动
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public int DownMoveMenu(int id)
        {
            Menu vMenu = GetById(id);
            var menuList = GetMenusByCategoryId(vMenu.CategoryId).OrderBy(m => m.Importance); ;

            Menu nextMenu = menuList.Where(m => m.ParentId == vMenu.ParentId &&
                m.Importance > vMenu.Importance).OrderBy(m => m.Importance).FirstOrDefault();


            if (nextMenu == null)
            {
                // 已经在最后一位
                return 0;
            }

            var num = nextMenu.Importance - vMenu.Importance;
            SetMenuImportance(vMenu.Id, num);
            SetMenuImportance(nextMenu.Id, -num);

            _cacheService.Invalidate(EntityNames.Menu);
            return 1;
        }

        /// <summary>
        /// 设置排序
        /// </summary>
        /// <param name="menuId"></param>
        /// <param name="num"></param>
        /// <returns></returns>
        public int SetMenuImportance(int menuId, int num)
        {
            int result;
            using (_connection = Utilities.GetOpenConnection())
            {
                var query = "with MenuSet_tree as ( " +
  "select MenuSet.id as top_MenuSet_id, MenuSet.id as MenuSet_id " +
  " from MenuSet union all   select top_MenuSet_id, MenuSet.id   from MenuSet_tree " +
  "     join MenuSet on MenuSet.ParentId = MenuSet_tree.MenuSet_id)  " +
"update MenuSet Set Importance = Importance + @Num where Id in (select MenuSet_id from MenuSet_tree where top_MenuSet_id = @MenuId)";
                result = (int)_connection.Execute(query, new { MenuId = menuId, Num = num });
            }
            return result;
        }

        /// <summary>
        /// 按分类重设排序
        /// </summary>
        /// <param name="categoryId"></param>
        public void ResetSort(int categoryId)
        {
            var menuList = GetLevelMenusByCategoryId(categoryId);// _menuRepository.GetFilteredElements(m => m.CategoryId == categoryId && m.ParentId == null, m => m.ChildMenus).OrderBy(m => m.Importance);
            var list = menuList.Where(m=>m.ParentId == null).OrderBy(m => m.Importance)
                .SelectDeep<Menu>(m => m.ChildMenus.OrderBy(g => g.Importance));

            int i = 0;
            foreach (var item in list)
            {
                item.Importance = i;
                Update(item);
                i++;
            }
         //   _menuRepository.UnitOfWork.Commit();
            _cacheService.Invalidate(EntityNames.Menu);
            // return menu;
        }

    }
}
